package yxy.apple.nio.server;

import java.net.SocketAddress;
import java.util.ArrayList;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.util.concurrent.DefaultThreadFactory;
import yxy.apple.nio.Initializer;

public class Server {
	private ServerBootstrap bootstrap = new ServerBootstrap();
	private boolean running = false;
	private ArrayList<ChannelFuture> futures = new ArrayList<>();

	/**
	 * @param initializer     初始化器
	 * @param acceptorThreads acceptor线程数，一般长连接业务可每个绑定地址对应一个，短连接业务可适当增加
	 * @param workThreads,    工作线程数，根据Netty优化建议，一般为CPU个数乘2，对于交互多计算少的网元，可适度增加
	 * @param bindings        绑定的本地地址，可多地址绑定同时进行一个服务
	 */
	public Server(Initializer initializer, int acceptorThreads, int workThreads) {
		NioEventLoopGroup acceptors = new NioEventLoopGroup(acceptorThreads,
				new DefaultThreadFactory(this.getClass().getSimpleName() + "-ServerAcceptorThread"));
		NioEventLoopGroup workers = new NioEventLoopGroup(workThreads,
				new DefaultThreadFactory(this.getClass().getSimpleName() + "-ServerWorkerThread"));
		this.bootstrap.group(acceptors, workers);
		this.bootstrap.channel(NioServerSocketChannel.class);
		this.bootstrap.childHandler(initializer);
	}

	public <T> void setBootstrapOption(ChannelOption<T> option, T value) {
		this.bootstrap.option(option, value);
	}

	public <T> void setChannelOption(ChannelOption<T> option, T value) {
		this.bootstrap.childOption(option, value);
	}

	/**
	 * 使用Netty默认的线程配置实例化Server <br />
	 * 
	 * @param initializer
	 */
	public Server(Initializer initializer) {
		this(initializer, 0, 0);
	}

	public void start(SocketAddress binding) throws Exception {
		this.start(new SocketAddress[] { binding });
	}

	public void start(SocketAddress[] bindings) throws Exception {
		if (!this.running) {
			try {
				for (SocketAddress binding : bindings) {
					ChannelFuture future = this.bootstrap.bind(binding).sync();
					this.futures.add(future);
					future.channel().closeFuture().addListener(new ChannelFutureListener() {
						@Override
						public void operationComplete(ChannelFuture future) throws Exception {
							future.channel().close();
						}
					});
				}
				this.running = true;
			} catch (Exception e) {
				this.stop();
				throw e;
			}
		}
	}

	public void stop() {
		if (running) {
			for (ChannelFuture future : this.futures) {
				if (future.channel() != null) {
					future.channel().close();
				}
			}
			this.futures.clear();
			this.bootstrap.config().childGroup().shutdownGracefully();
			this.running = false;
		}
	}

	public boolean running() {
		return this.running;
	}

	public void shutdown() {
		this.stop();
		this.bootstrap.config().group().shutdownGracefully();
	}
}
